home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’96 / FinderFlocks / FinderFun / Exceptions.cp < prev    next >
Encoding:
Text File  |  1996-06-22  |  7.7 KB  |  300 lines  |  [TEXT/MMCC]

  1. /*============================================================
  2.     Exceptions.c
  3.     
  4.     greggor@apple.com
  5.     
  6.     New API matches C++ exceptions as much as possible
  7.     
  8.     Usage:
  9.     
  10.     #include "Exceptions.h"
  11.     
  12.         Ptr ptr1 = nil;
  13.         Ptr ptr2 = nil;
  14.         OSErr err = noErr;
  15.         
  16.         NOREGISTER(ptr1);
  17.         NOREGISTER(ptr2);
  18.         
  19.         Try
  20.         {
  21.             ptr1 = NewPtr( 1024 * 1024 );
  22.             FailErr(MemError());
  23.             ptr2 = NewPtr( 15 );
  24.             FailErr(MemError());
  25.             
  26.             if(CatastrophicError())
  27.                 Throw(eCatastrophicError);
  28.         }
  29.         Catch(err)
  30.         {
  31.             //
  32.             //    Clean up here
  33.             //
  34.             if( ptr1 != nil )
  35.                 DisposPtr( ptr1 );
  36.             if( ptr2 != nil )
  37.                 DisposPtr( ptr2 );
  38.             
  39.             if(err == eCatastrophicError)
  40.                 Alert(blah blah blah);
  41.             
  42.             //
  43.             // Fail again...
  44.             //
  45.             Throw(err);
  46.         }
  47.  
  48. ============================================================*/
  49. #include <Types.h>
  50. #include <Memory.h>
  51. #include <Resources.h>
  52. #include <setjmp.h>
  53.  
  54. #include "Exceptions.h"
  55.  
  56. #define DEBUGMESSAGES 1
  57.  
  58. // #define FAILMESSAGES
  59.  
  60. TException*            gExceptionStack = nil;
  61. NotifyFailureProc    gNotifyProc = nil;
  62.  
  63. //--------------------------------------------------------------------------------
  64. // TException::Setup
  65. //
  66. // This function is called by the TRY macro
  67. //--------------------------------------------------------------------------------
  68. Boolean TException::Setup()
  69. {
  70.     Boolean firstTime = false;
  71.     
  72.     if(this->fMagicFailID == kMagicFailID)
  73.     {
  74.         if((this->fFlags & kExceptionThrown) == 0)
  75.         {
  76. #ifdef DEBUGMESSAGES
  77.             if((this->fFlags & kTryBlockHasBeenSetup) != 0)
  78.                 DebugStr("\pTry setup reentered for initialization in a bogus way");
  79. #endif
  80.                         
  81.             //
  82.             // Clear the 'is resignaling' bit of the
  83.             // failure stack
  84.             //
  85.             if(gExceptionStack != nil)
  86.                 gExceptionStack->fFlags &= ~kResignalingFailure;
  87.             
  88.             //
  89.             // Push the new entry onto the failure stack
  90.             //
  91.             this->fNext = gExceptionStack;
  92.             gExceptionStack = this;
  93.             
  94.             this->fFlags |= (kTryFrameOnFailureStack | kTryBlockHasBeenSetup);
  95.             firstTime = true;
  96.         }
  97.         else
  98.         {
  99. #ifdef DEBUGMESSAGES
  100.             if((this->fFlags & kTryBlockHasBeenSetup) == 0)
  101.                 DebugStr("\pTry setup reentered for cleanup in a bogus way");
  102. #endif
  103.             
  104.             this->TearDown();
  105.         }
  106.     }
  107. #ifdef DEBUGMESSAGES
  108.     else
  109.         DebugStr("\pCorrupt fail info in Setup");
  110. #endif
  111.  
  112.     return firstTime;
  113. } // TException::Setup
  114.  
  115. //--------------------------------------------------------------------------------
  116. // TException::BeginTry
  117. //
  118. // This function is called by the TRY macro
  119. //--------------------------------------------------------------------------------
  120. Boolean TException::BeginTry()
  121. {
  122.     Boolean firstTime = false;
  123.     
  124.     if(this->fMagicFailID == kMagicFailID)
  125.     {
  126.         if((this->fFlags & kTryInProgress) == 0)
  127.         {
  128.             this->fFlags |= kTryInProgress;
  129.             firstTime = true;
  130.         }
  131.         else
  132.         {
  133.             this->fFlags |= kTrySucceeded;
  134.             this->TearDown();
  135.         }
  136.     }
  137. #ifdef DEBUGMESSAGES
  138.     else
  139.         DebugStr("\pCorrupt fail info in Setup");
  140. #endif
  141.     
  142.     return firstTime;
  143. } // TException::BeginTry
  144.  
  145. //--------------------------------------------------------------------------------
  146. // TException::TearDown
  147. //
  148. // 'TearDown' is called right before the exception handling block
  149. // is called (in which case fFlags & kExceptionThrown will be set), or after
  150. // a try block has completed successfully (in which case fFlags & kTrySucceeded
  151. // will be set).
  152. //--------------------------------------------------------------------------------
  153. void TException::TearDown()
  154. {
  155.     if((gExceptionStack == this) && (this->fMagicFailID == kMagicFailID) && ((this->fFlags & kTryFrameOnFailureStack) != 0))
  156.     {
  157.         //
  158.         // Pop an entry off the top of the exception stack
  159.         //
  160.         gExceptionStack = gExceptionStack->fNext;
  161.         this->fFlags &= ~kTryFrameOnFailureStack;
  162.         
  163.         //
  164.         // If we failed & there is anything left on the stack,
  165.         // then note that we might want to resignal
  166.         //
  167.         if((gExceptionStack != nil) && ((this->fFlags & kExceptionThrown) != 0))
  168.         {
  169.             gExceptionStack->fFlags |= kResignalingFailure;
  170.             gExceptionStack->fError = this->fError;
  171.         }
  172.     }
  173. #ifdef DEBUGMESSAGES
  174.     else
  175.     {
  176.         if(gExceptionStack == nil)
  177.             DebugStr( "\pError -- failure stack inexplicably nil" );
  178.         else
  179.             DebugStr( "\pTearDownTry:  Failure handler stack corrupted" );
  180.     }
  181. #endif
  182. } // TException::TearDown
  183.  
  184. //--------------------------------------------------------------------------------
  185. // Throw
  186. //
  187. // Invoke exception handling
  188. //--------------------------------------------------------------------------------
  189. void Throw(OSErr theErr)
  190. {
  191.     //
  192.     // First do a little bit of sanity checking
  193.     //
  194.     if((gExceptionStack != nil) && (gExceptionStack->fMagicFailID == kMagicFailID))
  195.     {
  196.         //
  197.         // Don't allow Throw(noErr)
  198.         //
  199.         if(theErr == noErr)
  200.             theErr = eGeneralErr;
  201.         
  202.         //
  203.         // Notify that we failed, unless we are resignalling
  204.         // Note:  If 'kResignalingFailure' is set, then fError
  205.         // will be the error we set last time.  Optionally, we
  206.         // could require that 'theErr == gExceptionStack->fError'
  207.         // in order to do notification, but we didn't.
  208.         //
  209.         if((gExceptionStack->fFlags & kResignalingFailure) == 0)
  210.             NotifyFailure(theErr);
  211.         
  212.         //
  213.         // Remember the error that caused the failure
  214.         //
  215.         gExceptionStack->fError = theErr;
  216.         
  217.         //
  218.         // TRY does a setjmp; now we longjmp back to
  219.         // that point with a longjmp.  The 'TRY' macro
  220.         // includes an 'if' statement that is true
  221.         // the first time through and false when we longjmp
  222.         // back to it; the EXCEPTion handler is just the
  223.         // 'else' branch of that 'if' block.
  224.         //
  225.         // Note:    This code used to use the result passed
  226.         //            back from longjmp, but that didn't work
  227.         //            under MetroWerks.  My guess is that MetroWerks
  228.         //            was confused about the size of an int,
  229.         //            probably due to the libraries I chose to
  230.         //            link with.  Rather than work out the problem,
  231.         //            I decided to stop using the return value
  232.         //            from longjmp.
  233.         //
  234.         gExceptionStack->fFlags |= kExceptionThrown;
  235.         longjmp(gExceptionStack->fEnv, kExceptionThrown);
  236.     }
  237. #ifdef DEBUGMESSAGES
  238.     else
  239.     {
  240.         if(gExceptionStack == nil)
  241.             DebugStr("\pthrow without try");
  242.         else
  243.             DebugStr( "\pthrow:  Failure handler stack corrupted" );
  244.     }
  245. #endif
  246. } // Throw
  247.  
  248. //--------------------------------------------------------------------------------
  249. // SetExceptionNotifyProc
  250. //
  251. // Install a single routine called at failure time before
  252. // exception processing begins.  This function is used in
  253. // conjunction with ShowStackChain to record and later show
  254. // the stack at the time of failure (left over from old 68K
  255. // code that doesn't work too well on PowerPC).
  256. //--------------------------------------------------------------------------------
  257. void SetExceptionNotifyProc(NotifyFailureProc notifyProc)
  258. {
  259.     gNotifyProc = notifyProc;
  260. } // SetExceptionNotifyProc
  261.  
  262. //--------------------------------------------------------------------------------
  263. // ExceptionNotifyProcInstalled
  264. //
  265. // This routine is used by ReportError so that it knows whether or
  266. // not it should show the 'sc6' button
  267. //--------------------------------------------------------------------------------
  268. Boolean ExceptionNotifyProcInstalled(void)
  269. {
  270.     return gNotifyProc != nil;
  271. } // ExceptionNotifyProcInstalled
  272.  
  273. //--------------------------------------------------------------------------------
  274. // NotifyFailure
  275. //
  276. // Notify that an error occured
  277. //--------------------------------------------------------------------------------
  278. void NotifyFailure(OSErr err)
  279. {
  280. #ifdef FAILMESSAGES
  281.     DebugPrintf("Failure caused by an error %d", err);
  282. #endif
  283.  
  284.     // DebugStr("\pSomeone is about to fail...");
  285.  
  286.     if(gNotifyProc != nil)
  287.         (*gNotifyProc)(err);
  288. } // NotifyFailure
  289.  
  290. //--------------------------------------------------------------------------------
  291. // MakeVariableNoRegister
  292. //
  293. // This routine doesn't actually do anything; it's just used
  294. // by the NOREGISTER macro
  295. //--------------------------------------------------------------------------------
  296. void MakeVariableNoRegister( void* foo )
  297. {
  298.  
  299. } // MakeVariableNoRegister
  300.